#include "flowscene.h"
#include <QGraphicsItem>
#include "flownode.h"
#include "flowedge.h"
#define INVISIBLEROOT "#invisible-root"

//每个ActionNode的详细信息.
class NodeInfo
{
public:
    NodeInfo():mext(0),item(NULL),isVoidNode(false)
    {}
    //最大的next索引。例如2，那么最多有0，1，2三个.
    int mext;
    QString name;
    FlowNode* item;
    QJsonObject param;
    QList<NodeInfo*> next;
    QList<FlowEdge*> link;

    bool isVoidNode; //标记是否虚节点.虚节点的link是虚的，从父node指过来.
    //int mext;  此模式下代表index，是父对象哪一条next
};

FlowScene::FlowScene()
    : root_(NULL)
    , selected_(NULL)
    , focused_(NULL)
{
    root_ = new NodeInfo();
    root_->mext = 0;
    root_->name = INVISIBLEROOT;
    root_->item = new FlowNode();
    root_->item->setId(INVISIBLEROOT);
    root_->item->setPos(0,-50);
    //root_->item->setVisible(false);
    focused_ = root_;

    nodeAdjustTimer_.setInterval(1000 / 25);
    nodeAdjustTimer_.setSingleShot(false);
    connect(&nodeAdjustTimer_,&QTimer::timeout,[=](){
        QList<FlowNode *> nodes;
        QList<QGraphicsItem *> hitItems = items();
        foreach (QGraphicsItem *item, hitItems) {
            if (item->type()==ItemNode)
                nodes << (FlowNode*)item;
        }

        NodeInfo* fixedNode = NULL;  //固定节点不动.
        if(nodes_.count()){
            fixedNode = root_->next.at(0);
        }
        else{
            fixedNode = voids_.at(0);
        }
        Q_ASSERT(fixedNode);
        fixedNode->item->setPos(0,0);
        foreach (FlowNode *node, nodes)
        {
            if(node != fixedNode->item)
            {
                node->calculateForces();
            }
        }

        bool itemsMoved = false;
        foreach (FlowNode *node, nodes) {
            if (node->advance())
                itemsMoved = true;
        }

        if(!itemsMoved){
            nodeAdjustTimer_.stop();
        }
    });
}

FlowScene::~FlowScene()
{
    _clearItems();
    delete root_->item;
    delete root_;
}

void FlowScene::resetItems()
{
    _clearItems();
    //_setSelected(INVISIBLEROOT);
    _extendNode(root_);
     focused_ = root_;
    FlowNode* startVoid = voids_.at(0)->item;
    startVoid->setPos(0,0);
}

int FlowScene::loadItems(const QJsonArray& jArr,
                         const QString &rootID)
{
    int temp = 0;
    //clearItems();
    Q_ASSERT(nodes_.count()==0);
    //把对象读出，构建链接网络.
    foreach(const QJsonValue &jval ,jArr)
    {
        QJsonObject jobj = jval.toObject();
        QString id = jobj.value("id").toString();
        if(nodes_.contains(id)){
            qInfo()<<"id conflict!"<<id;
            return -1;
        }
        QString name = jobj.value("name").toString();
        int mext = jobj.value("mext").toInt();
        NodeInfo *ninfo= new NodeInfo();
        ninfo->item = new FlowNode();
        ninfo->item->setId(id);
        ninfo->item->setPos(qPow(-1,temp)*temp*20, (++temp)*50);
        ninfo->mext = mext;
        ninfo->name = name;
        ninfo->param = jobj.value("param").toObject();
        //ninfo->item->setId(jobj.);
        nodes_.insert(id,ninfo);
    }

    //处理根节点和开始节点.
    NodeInfo *startNode = nodes_.value(rootID);
    if(startNode == NULL){
        qInfo()<<"can't find node of root"<<rootID;
        return -1;
    }
    Q_ASSERT(root_->next.count() == 0);
    startNode->item->setPos(0,0);
    addItem(startNode->item);
    FlowEdge *edge = new FlowEdge(root_->item,startNode->item);
    //edge->setVisible(false);  //不加入scene，无需visible
    root_->next << startNode;
    root_->link << edge;
    //root_->item->addEdge(edge);

    //第二遍完成id到对象指针的切换.
    foreach(const QJsonValue &jval ,jArr)
    {
        QJsonObject jobj = jval.toObject();
        QString id = jobj.value("id").toString();
        NodeInfo *ninfo = nodes_.value(id);
        Q_ASSERT(ninfo != NULL);
        if(ninfo == NULL) continue;
        addItem(ninfo->item);
        QJsonArray nxtArr = jobj.value("next").toArray();
        Q_ASSERT(ninfo->next.count() == 0);
        for(int i=0;i<=ninfo->mext;i++)
        {
            QString nxtId;
            if(i<nxtArr.count()){
                nxtId = nxtArr.at(i).toString();
            }
            if(nxtId.isEmpty())
            {
                //为空或者空串. 创建虚化节点.
                //FlowNode* voidNode = new FlowNode();
                //ninfo->next << voidNode;
                ninfo->next << NULL;
            }
            else{
                if(false == nodes_.contains(nxtId)){
                    qInfo()<<"node"<<nxtId<<"missing.";
                    resetItems();
                    return -1;
                }
                NodeInfo* nxtItem = nodes_.value(nxtId);
                FlowEdge *edge = new FlowEdge(ninfo->item,nxtItem->item);
                ninfo->link << edge;
                //ninfo->item->addEdge(edge);
                //addItem(nxtItem->item);
                addItem(edge);
                ninfo->next << nxtItem;
            }
        }
    }
    setSelected(rootID);
    if(nodes_.count())
    {
        activeNodeAdjust();
    }
    return nodes_.count()-voids_.count();
}

void FlowScene::setSelected(const QString &id)
{
    if(nodes_.contains(id) == false) {
        qInfo()<<"select"<<id<<"not exist.";
        return;
    }
    if(selected_){
        if(selected_->item->id() == id) return;
        _setSelected(QString());
    }
    _setSelected(id);
    emit sigSelceted();
 }

void FlowScene::_clearItems()
{
    if(focused_)
    {
        _pinchNode(focused_);
    }
     Q_ASSERT(voids_.count()==0);
    //清理root_
    if(root_)
    {
        foreach(FlowEdge* edge, root_->link)
        {
            delete edge;
        }
        root_->link.clear();
        root_->next.clear();
    }
    //清理items
    foreach(NodeInfo* ninfo, nodes_)
    {
        foreach(FlowEdge* edge, ninfo->link)
        {
            delete edge;
        }
        ninfo->link.clear();
    }
    foreach(NodeInfo* ninfo, nodes_)
    {
        delete ninfo->item;
        delete ninfo;
    }
    nodes_.clear();
}

void FlowScene::_setSelected(const QString &id)
{
    Q_ASSERT(focused_);
    if(id.isEmpty())
    { //去除选中.
        Q_ASSERT(selected_);
        if(selected_ == NULL) return;
        selected_->item->setSelected(false);
        selected_ = NULL;
    }
    else
    {
        //注意，一定要先反选再正选，所以selected_一定要NULL.
        Q_ASSERT(selected_ == NULL);
        if(selected_){
            qInfo()<<"select id:"<<id<<"already exist:"<<selected_->item->id();
            return;
        }

        selected_ = nodes_.value(id);
        if(selected_ == NULL){
            qInfo()<<"select id:"<<id<<"not exist";
            return;
        }

        if(selected_->isVoidNode)
        {//如果是虚节点，代表一定已经展开，只是换一个虚节点选中.
            Q_ASSERT(selected_->next.at(0) == focused_);
            selected_->item->setSelected(true);
        }
        else
        {//代表有可能换一个node选中.
            focused_->item->setSelected(false);
            if(selected_ != focused_)
            {
                _pinchNode(focused_);
                _extendNode(selected_);
            }
            selected_->item->setSelected(true);
            focused_ = selected_;
        }
    }
}

void FlowScene::_extendNode(NodeInfo *einfo)
{
    if(einfo->isVoidNode) return;
    Q_ASSERT(voids_.count()==0);
    //einfo->item->setSelected(true);
    //为空节点创建虚link和虚节点.
    for(int i=0;i<=einfo->mext;i++)
    {
        const QList<NodeInfo*>& nlist = einfo->next;
        if(i<nlist.count() && nlist.at(i) &&
                nlist.at(i)->item->id().isEmpty()==false){
            continue;
        }
        QString voidid = _genNodeID("Void#");
        NodeInfo *ninfo = new NodeInfo();
        ninfo->isVoidNode = true;
        ninfo->item = new FlowNode();
        ninfo->item->setId(voidid);
        ninfo->item->setVoid(true);
        ninfo->item->setPos(einfo->item->x()+qPow(-1,i)*i*20,
                            einfo->item->y()+40);
        ninfo->mext = i;
        ninfo->name = voidid;
        FlowEdge *edge = new FlowEdge(einfo->item,ninfo->item);
        //void 节点的next反过来指向父.有且仅有1个
        ninfo->link << edge;
        ninfo->next << einfo;
        //einfo->item->addEdge(edge);
        addItem(ninfo->item);
        addItem(edge);
        nodes_.insert(voidid,ninfo);
        voids_ << ninfo;
    }

    if(voids_.count())
    {
        activeNodeAdjust();
    }
}

void FlowScene::_pinchNode(NodeInfo *pinfo)
{
    if(pinfo->isVoidNode==true) return;
    foreach(NodeInfo* ninfo, voids_)
    {
        Q_ASSERT(ninfo->isVoidNode);
        Q_ASSERT(ninfo->link.count()==1 && ninfo->next.count()==1);
        Q_ASSERT(ninfo->next.at(0) == pinfo);
        FlowEdge *edge = ninfo->link.at(0);
        delete edge;
        nodes_.remove(ninfo->item->id());
        delete ninfo->item;
        delete ninfo;
    }
    voids_.clear();
}

QString FlowScene::_genNodeID(const QString &suffix) const
{
    static int counter = 0;
    QString id = suffix;
    id += QString::number(++counter);
    return id;
}

void FlowScene::activeNodeAdjust()
{
    if(nodeAdjustTimer_.isActive()==false)
    {
        nodeAdjustTimer_.start();
    }
}

void FlowScene::onClicked(FlowNode *node)
{
    setSelected(node->id());
}

QString FlowScene::getSelectedName() const
{
    return selected_->name;
}

QString FlowScene::getSelectedId() const
{
    return selected_->item->id();
}

QStringList FlowScene::getAllItemIds() const
{
    return nodes_.keys();
}

QJsonObject FlowScene::getSelectedItemParam() const
{
    return selected_->param;
}

QStringList FlowScene::getSelectedItemNext() const
{
    QStringList nxtIds;
    if(selected_->isVoidNode) return nxtIds;
    foreach(NodeInfo *ninfo, selected_->next)
    {
        if(ninfo && ninfo->isVoidNode==false){
            nxtIds << ninfo->item->id();
        }
        else{
            nxtIds << "";
        }
    }
    return nxtIds;
}

void FlowScene::renameSelectedId(QString newid)
{
    QString oldid = selected_->item->id();
    NodeInfo* ninfo = nodes_.value(oldid);
    nodes_.remove(oldid);
    selected_->item->setId(newid);
    nodes_.insert(newid,ninfo);
}

void FlowScene::touchPoints(QTouchEvent::TouchPoint touchPoint, QGraphicsItem *hit, qint64 time)
{
    if(hit->type() == ItemNode)
    {
        switch (touchPoint.state()) {
        case Qt::TouchPointPressed:
            break;
        case Qt::TouchPointMoved:
            break;
        case Qt::TouchPointReleased:
            onClicked((FlowNode*)hit);
            break;
        default:
            break;
        }
    }

}

void FlowScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
    if(mTouchOrMouse<=MouseModeSwiftN){
        if(event->button()!=Qt::LeftButton)
            QGraphicsScene::mouseMoveEvent(event);
        return;
    }
    QTouchEvent::TouchPoint tPoint(0);
    tPoint.setPos(event->scenePos());
    tPoint.setLastPos(event->lastPos());
    tPoint.setScenePos(event->scenePos());
    tPoint.setLastScenePos(event->lastScenePos());
    tPoint.setState(Qt::TouchPointMoved);
    QList<QTouchEvent::TouchPoint> tList;        tList << tPoint;
    QTouchEvent tEvent(QEvent::TouchUpdate,0,0,0,tList);
    touchEvent(&tEvent);
}

void FlowScene::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    mTouchOrMouse++;
    if(mTouchOrMouse<=MouseModeSwiftN){
        if(event->button()!=Qt::LeftButton)
            QGraphicsScene::mousePressEvent(event);
        return;
    }
    //qDebug()<<event->pos(); scene事件的pos总为0
    QTouchEvent::TouchPoint tPoint(0);
    tPoint.setPos(event->scenePos());
    tPoint.setScenePos(event->scenePos());
    tPoint.setState(Qt::TouchPointPressed);
    QList<QTouchEvent::TouchPoint> tList;        tList << tPoint;
    QTouchEvent tEvent(QEvent::TouchBegin,0,0,0,tList);
    touchEvent(&tEvent);
}

void FlowScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
    if(mTouchOrMouse<=MouseModeSwiftN){
        if(event->button()!=Qt::LeftButton)
            QGraphicsScene::mouseReleaseEvent(event);
        return;
    }
    QTouchEvent::TouchPoint tPoint(0);
    tPoint.setPos(event->scenePos());
    tPoint.setScenePos(event->scenePos());
    tPoint.setStartScenePos(event->buttonDownScenePos(Qt::LeftButton));
    QList<QTouchEvent::TouchPoint> tList;        tList << tPoint;
    tPoint.setState(Qt::TouchPointReleased);
    QTouchEvent tEvent(QEvent::TouchEnd,0,0,0,tList);
    touchEvent(&tEvent);
}

bool FlowScene::event(QEvent *event)
{
    switch(event->type())
    {
    case QEvent::TouchEnd:
    case QEvent::TouchBegin:
    case QEvent::TouchUpdate:
        mTouchOrMouse = 0;
        touchEvent(static_cast<QTouchEvent*>(event));
        return true;
    default:
        return QGraphicsScene::event(event);
    }
}

void FlowScene::touchEvent(QTouchEvent *event)
{
    _touchEvent(event,false);
}

void FlowScene::_touchEvent(QTouchEvent *event, bool bSimu)
{
    QList<QTouchEvent::TouchPoint> touchPoints = event->touchPoints();
    qint64 time = mElapsed.elapsed();
    foreach (const QTouchEvent::TouchPoint &touchPoint, touchPoints)
    {
        QGraphicsItem* item = nullptr;
        if(touchPoint.state() == Qt::TouchPointPressed)
        {
            QList<QGraphicsItem *> hitList = items(touchPoint.pos());
            foreach(item, hitList)
            {
                //qDebug()<<"item type" << item->type();
                if(item->type() <= ItemIgnoreHit) continue;
                break;
            }

            //item = this->itemAt(touchPoint.pos(),QTransform());
            //while(item && item->type() <= ItemIgnoreHit) //QGraphicsItem::UserType)
            //{
            //    item = item->parentItem();
            //}
            if(item) mId2Hit.insert(touchPoint.id(),item);
        }
        else
        {
            item = mId2Hit.value(touchPoint.id(),nullptr);
        }

        if(item)
        {
            this->touchPoints(touchPoint,item,time);
        }

        if(touchPoint.state() == Qt::TouchPointReleased)
        {
            mId2Hit.remove(touchPoint.id());
        }
    }
}
